﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Data.Common;
using System.Threading;
using System.Collections;
using System.Globalization;

namespace ResourceBlender_Express {
    /// <summary>
    /// Copies translations from language to another.
    /// There may be a better way of doing this. Since the Auto Translator uses the same dialog but a different method. Possibly wire up some sort of delegate to a property of the window and use that
    /// </summary>
    public partial class WinCreateVariantLog : Window {
        /// <summary>
        /// Gets whether the translation was completed successfully.
        /// </summary>
        /// <value><c>true</c> if successful; otherwise, <c>false</c>.</value>
        public bool Success { get { return success; } }

        private Style styError,
                      stySuccess, // every successful query/translation
                      styStatus, // intermediate status messages
                      styOther;
        private BackgroundWorker worker;
        private string sourceLanguage, 
                       destinationLanguage;
        private bool overwriteExisting;
        private bool success = false;

        /// <summary>
        /// Initializes a new <see cref="WinCreateVariantLog"/>.
        /// </summary>
        public WinCreateVariantLog(string sourceLanguage, string destinationLanguage, bool overwriteExisting) {
            InitializeComponent();
            this.sourceLanguage = sourceLanguage;
            this.destinationLanguage = destinationLanguage;
            this.overwriteExisting = overwriteExisting;
            styError = this.FindResource("ErrorStyle") as Style;
            stySuccess = this.FindResource("SuccessStyle") as Style;
            styStatus = this.FindResource("StatusStyle") as Style;
            styOther = this.FindResource("OtherStyle") as Style;
        }

        /// <summary>
        /// Begins translation when the window has finished loading.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e) {
            worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.WorkerSupportsCancellation = true;
            worker.ProgressChanged += (s, pce) => {
                if(pce.UserState is StatusMessage) {
                    StatusMessage message = pce.UserState as StatusMessage;
                    LogText(message.Message, message.MessageType);
                }
            };
            worker.DoWork += (s, dwe) => {
                StatusMessage.StatusChangedEventHandler logHandler = new StatusMessage.StatusChangedEventHandler(message => {
                    worker.ReportProgress(0, message);
                });
                Database db = null;
                string sourceEnglishName,
                       destinationEnglishName;
                try {
                    sourceEnglishName = CultureInfo.GetCultureInfo(sourceLanguage).EnglishName;
                } catch {
                    sourceEnglishName = sourceLanguage;
                }
                try {
                    destinationEnglishName = CultureInfo.GetCultureInfo(destinationLanguage).EnglishName;
                } catch {
                    destinationEnglishName = destinationLanguage;
                }
                try {
                    db = new Database();
                    db.OpenConnection();
                    using(Database.Command cmd = db.CreateCommand()) {
                        cmd.CommandText = string.Format("SELECT resourceid, translation FROM {0}resources WHERE language = @lang", db.TablePrefix);
                        cmd.AddParameterWithValue("@lang", sourceLanguage);
                        Dictionary<object, object> resources = new Dictionary<object, object>();
                        DbDataReader reader = cmd.ExecuteReader();
                        while(reader.Read()) {
                            resources.Add(reader[0], reader[1]);
                        }
                        reader.Close();
                        cmd.Parameters["@lang"].Value = destinationLanguage;
                        cmd.AddParameterWithValue("@tID", "");
                        cmd.AddParameterWithValue("@trans", "");
                        cmd.AddParameterWithValue("@comment", "");
                        int numCopied = 0;
                        if(overwriteExisting) {
                            switch(db.InstanceType) {
                                case Database.DatabaseType.MySQL:
                                    cmd.CommandText = string.Format("INSERT INTO {0}resources (resourceid, language, translation, translatorcomment) VALUES (@tID, @lang, @trans, @comment) ON DUPLICATE KEY UPDATE translation = @trans, translatorcomment = @comment", db.TablePrefix);
                                    break;
                                case Database.DatabaseType.SQLServer:
                                    cmd.CommandText = string.Format("UPDATE {0}resources SET translation = @trans, translatorcomment = @comment WHERE resourceid = @tID AND language = @lang; IF @@ROWCOUNT = 0 INSERT INTO {0}resources (resourceid, language, translation, translatorcomment) VALUES (@tID, @lang, @trans, @comment)", db.TablePrefix);
                                    break;
                                case Database.DatabaseType.SQLite:
                                    cmd.CommandText = string.Format("INSERT OR REPLACE INTO {0}resources (resourceid, language, translation, translatorcomment) VALUES (@tID, @lang, @trans, @comment)", db.TablePrefix);
                                    break;
                            }
                            foreach(KeyValuePair<object, object> resource in resources) { // <resource ID, translation>
                                cmd.Parameters["@tID"].Value = resource.Key;
                                cmd.Parameters["@trans"].Value = resource.Value;
                                try {
                                    cmd.ExecuteNonQuery();
                                    logHandler(new StatusMessage("Overwriting translation for resource #" + resource.Key + "...", StatusMessage.StatusMessageType.Success));
                                    numCopied++;
                                } catch(Exception ex) { // any errors are SQL errors
                                    logHandler(new StatusMessage("Error adding translation for resource #" + resource.Key + ": " + ex.Message, StatusMessage.StatusMessageType.Error));
                                }
                                if(worker.CancellationPending) {
                                    throw new Exception("Aborted");
                                }
                            }
                        } else {
                            cmd.CommandText = string.Format("INSERT INTO {0}resources (resourceid, language, translation, translatorcomment) VALUES (@tID, @lang, @trans, @comment)", db.TablePrefix);
                            foreach(KeyValuePair<object, object> resource in resources) { // <resource ID, translation>
                                cmd.Parameters["@tID"].Value = resource.Key;
                                cmd.Parameters["@trans"].Value = resource.Value;
                                try {
                                    cmd.ExecuteNonQuery();
                                    logHandler(new StatusMessage("Copied translation for resource #" + resource.Key + "...", StatusMessage.StatusMessageType.Success));
                                    numCopied++;
                                } catch { // errors thrown when a translation exists
                                    logHandler(new StatusMessage("Translation for resource #" + resource.Key + " exists, skipping...", StatusMessage.StatusMessageType.Error));
                                }
                            }
                            if(worker.CancellationPending) {
                                throw new Exception("Aborted");
                            }
                        }
                        logHandler(new StatusMessage(string.Format("Copied {0} translations from {1} to {2}", numCopied, sourceEnglishName, destinationEnglishName), StatusMessage.StatusMessageType.Status));
                    }
                } catch(Exception ex) {
                    dwe.Result = ex.Message;
                    logHandler(new StatusMessage(ex.Message, StatusMessage.StatusMessageType.Error));
                } finally {
                    if(db != null) {
                        try {
                            db.CloseConnection();
                        } catch { }
                    }
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                if(rwe.Result != null && rwe.Result is string) {
                    DialogBox.ShowAlert(this, rwe.Result.ToString(), "Error");
                } else {
                    this.success = true;
                }
                prgStatus.Visibility = Visibility.Hidden;
                btnAbort.Visibility = Visibility.Collapsed;
                btnClose.Visibility = Visibility.Visible;
                txtProgress.Focus();
            };
            txtProgress.Inlines.Clear();
            prgStatus.Visibility = Visibility.Visible;
            btnClose.Visibility = Visibility.Collapsed;
            btnAbort.Visibility = Visibility.Visible;
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Determines whether the background thread can be aborted
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteAbort(object sender, CanExecuteRoutedEventArgs e) {
            e.CanExecute = worker != null && worker.IsBusy && !worker.CancellationPending;
        }

        /// <summary>
        /// Aborts the BackgroundWorker.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Abort(object sender, RoutedEventArgs e) {
            if(worker != null && worker.IsBusy && worker.WorkerSupportsCancellation) {
                try {
                    worker.CancelAsync();
                } catch { }
            }
        }

        /// <summary>
        /// Appends text to the log textbox.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <param name="error"><c>true</c> if the text is an error.</param>
        private void LogText(string text, StatusMessage.StatusMessageType type) {
            Run run = new Run(text + "\n");
            switch(type) {
                case StatusMessage.StatusMessageType.Error:
                    run.Style = styError;
                    break;
                case StatusMessage.StatusMessageType.Success:
                    run.Style = stySuccess;
                    break;
                case StatusMessage.StatusMessageType.Status:
                    run.Style = styStatus;
                    break;
                case StatusMessage.StatusMessageType.Other:
                    run.Style = styOther;
                    break;
            }
            txtProgress.Inlines.Add(new Span(run));
        }

        /// <summary>
        /// Closes the window.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void CloseWindow(object sender, ExecutedRoutedEventArgs e) {
            Close();
        }
    }
}
